#!/usr/bin/env python
#
# Copyright (c) 2008, Eric Chastain All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without 
# modification, are permitted provided that the following conditions 
# are met:
# 
# Redistributions of source code must retain the above copyright notice, 
# this list of conditions and the following disclaimer.
#
# Redistributions in binary form must reproduce the above copyright notice, 
# this list of conditions and the following disclaimer in the documentation 
# and/or other materials provided with the distribution.
# The name of the author may not be used to endorse or promote products 
# derived from this software without specific prior written permission.
# 
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
# THE POSSIBILITY OF SUCH DAMAGE.


# MU text editor

# References
# http://wxpython.org/docs/api/wx.stc.StyledTextCtrl-class.html
# http://wiki.wxpython.org/Optimizing for Mac OS X
# http://docs.python.org/lib/module-webbrowser.html
# http://www.zetcode.com/wxpython/menustoolbars/

__author__ = "Eric Chastain (mu@ericchastain.org)"

import cStringIO
import logging # http://docs.python.org/lib/node406.html
import os
import plistlib
import sys
import urllib
import webbrowser
import wx
import wx.aui
#import wx.html

from editor import *
from settings import *

# Setup logging
logging.basicConfig(level 		= logging.DEBUG,
                    format 		= "%(asctime)s %(levelname)s %(message)s",
                    filename 	= "mu.log",
                    filemode 	= "w")

console = logging.StreamHandler()
console.setLevel(logging.INFO)
formatter = logging.Formatter("%(name)-12s: %(levelname)-8s %(message)s")
console.setFormatter(formatter)
logging.getLogger("").addHandler(console)

# Setup some categories for error logging
# Call using (logging.UI("foo") or logging.fileEvents("foo")
General		= logging.getLogger("General")
UI 			= logging.getLogger("UI Events")
fileEvents 	= logging.getLogger("File Events")


# ========================================================================
# IDs for main menu
# ========================================================================

# File
ID_FILE_NEW = wx.NewId()
#ID_FILE_NEW_FROM_TEMPLATE_EDIT_TEMPLATES = wx.NewId()
ID_FILE_OPEN = wx.NewId()
ID_FILE_OPEN_RECENT_CLEAR_MENU = wx.NewId()
ID_FILE_CLOSE = wx.NewId()
ID_FILE_CLOSE_ALL_TABS = wx.NewId()
ID_FILE_SAVE = wx.NewId()
ID_FILE_SAVE_AS = wx.NewId()
ID_FILE_SAVE_ALL = wx.NewId()
ID_FILE_NEW_PROJECT = wx.NewId()
ID_FILE_NEW_PROJECT_FROM_TEMPLATE_EDIT_TEMPLATES = wx.NewId()
#ID_FILE_SAVE_PROJECT = wx.NewId()
#ID_FILE_SAVE_PROJECT_AS = wx.NewId()
ID_FILE_CLOSE_PROJECT = wx.NewId()
ID_FILE_EXIT = wx.ID_EXIT

# Edit
ID_EDIT_UNDO = wx.NewId()
ID_EDIT_REDO = wx.NewId()
ID_EDIT_CUT = wx.NewId()
ID_EDIT_COPY = wx.NewId()
ID_EDIT_PASTE = wx.NewId()
#M_ID_EDIT_PASTE_FROM_HISTORY = wx.NewId() # Wishlist item
ID_EDIT_SELECT_ALL = wx.NewId()
ID_EDIT_MODE_INSERT = wx.NewId()
ID_EDIT_MODE_OVERWRITE = wx.NewId()
#ID_EDIT_SELECT = wx.NewId()
#ID_EDIT_SELECT_WORD = wx.NewId()
#ID_EDIT_SELECT_LINE = wx.NewId()
#ID_EDIT_SELECT_PARAGRAPH = wx.NewId()
#ID_EDIT_SELECT_ENCLOSING_BRACKETS = wx.NewId()
#ID_EDIT_SELECT_CURRENT_SCOPE = wx.NewId()
ID_EDIT_FIND = wx.NewId()
ID_EDIT_DEFINE = wx.NewId()
ID_EDIT_FIND_ON_WEB = wx.NewId()
#ID_EDIT_FIND_IN_PROJECT = wx.NewId()
ID_EDIT_FIND_NEXT = wx.NewId()
ID_EDIT_FIND_PREVIOUS = wx.NewId()
ID_EDIT_FIND_REPLACE_ALL = wx.NewId()
ID_EDIT_FIND_REPLACE_IN_SELECTION = wx.NewId()
ID_EDIT_PREFERENCES = wx.ID_PREFERENCES

ID_VIEW_PROJECT = wx.NewId()
ID_VIEW_OUTPUT = wx.NewId()
ID_VIEW_FONT_BIGGER = wx.NewId()
ID_VIEW_FONT_SMALLER = wx.NewId()
ID_VIEW_GUTTER_FOLDINGS = wx.NewId()
ID_VIEW_GUTTER_LINE_NUMBERS = wx.NewId()
ID_VIEW_SOFT_WRAP = wx.NewId()
ID_VIEW_WRAP_COLUMN_SHOW_INDICATOR = wx.NewId()
ID_VIEW_WRAP_COLUMN_40 = wx.NewId()
ID_VIEW_WRAP_COLUMN_78 = wx.NewId()
ID_VIEW_WRAP_COLUMN_OTHER = wx.NewId()
ID_VIEW_LINE_ENDINGS = wx.NewId()
ID_VIEW_SHOW_INVISIBLES = wx.NewId()
ID_VIEW_FOLD_ALL = wx.NewId()

# Tools
#ID_TOOLS_GO = wx.NewId()

# Navigate
ID_NAVIGATE_NEXT_FILE_TAB = wx.NewId()
ID_NAVIGATE_PREVIOUS_FILE_TAB = wx.NewId()
ID_NAVIGATE_GO_TO_LINE = wx.NewId()
#ID_NAVIGATE_OPEN_SELECTED_FILE = wx.NewId()

ID_HELP_MU_HELP = wx.ID_HELP
ID_HELP_ABOUT = wx.ID_ABOUT

#ID_HELP_DEBUG = wx.NewId()

# ========================================================================
# MU Main Frame
# ========================================================================
class MU(wx.Frame):
	'''
	Assembles the main mu window. Initializes window to settings stored
	in mu.configure. Hooks menus to event handling routines, and contains
	all event handling functions.
	'''
	muroot = os.path.abspath(".")

	# Global Editor Settings
	filesOpen = [] # Holds paths to open files and dirty bits.


	'''
	def GetMondrianData():
		return \
	'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\
	\x00\x00szz\xf4\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\x00qID\
	ATX\x85\xed\xd6;\n\x800\x10E\xd1{\xc5\x8d\xb9r\x97\x16\x0b\xad$\x8a\x82:\x16\
	o\xda\x84pB2\x1f\x81Fa\x8c\x9c\x08\x04Z{\xcf\xa72\xbcv\xfa\xc5\x08 \x80r\x80\
	\xfc\xa2\x0e\x1c\xe4\xba\xfaX\x1d\xd0\xde]S\x07\x02\xd8>\xe1wa-`\x9fQ\xe9\
	\x86\x01\x04\x10\x00\\(Dk\x1b-\x04\xdc\x1d\x07\x14\x98;\x0bS\x7f\x7f\xf9\x13\
	\x04\x10@\xf9X\xbe\x00\xc9 \x14K\xc1<={\x00\x00\x00\x00IEND\xaeB`\x82' 

	def GetMondrianBitmap(self):
		return wx.BitmapFromImage(GetMondrianImage())

	def GetMondrianImage(self):
		stream = cStringIO.StringIO(GetMondrianData())
		return wx.ImageFromStream(stream)

	def GetMondrianIcon(self):
		icon = wx.EmptyIcon()
		icon.CopyFromBitmap(GetMondrianBitmap())
		return icon
	'''

	def __init__(self, parent, id = -1, title = "", 
		pos=wx.DefaultPosition, size=wx.DefaultSize, 
		style=wx.DEFAULT_FRAME_STYLE | wx.NO_BORDER | wx.CLIP_CHILDREN):

		wx.Frame.__init__(self, parent, id, title, pos, size, style)

		# Read our configuration file here so we can manipulate it at will.
		self.configuration = plistlib.readPlist("mu.configure")

		self.version = " - June 2008"

		General.info("Initializing mu...")

		# Load Icon
		self.SetIcon(wx.Icon(self.muroot +  "/art/mu.ico", wx.BITMAP_TYPE_ICO))

		# Dic to hold {wx.NewId() <-> full file path} for all file templates
		self.FileTemplates = {}

		# Load Height and Width from config
		Height = self.configuration["UI"]["Height"]
		Width = self.configuration["UI"]["Width"]

		# Load Initial X and Y position from config
		InitX = self.configuration["UI"]["InitX"]
		InitY = self.configuration["UI"]["InitY"]

		self.SetSize(wx.Size(int(Width), int(Height)))
		self.Move(wx.Point(int(InitX), int(InitY)))

		# Setup Frame Manager
		self._mgr = wx.aui.AuiManager()
		self._mgr.SetManagedWindow(self)
		self.n = 0
		self.x = 0



		# ================================================================
		# Setup the MenuBar
		# ================================================================

		self.menubar = wx.MenuBar()

		# File
		File = wx.Menu()
		self.File_New = wx.MenuItem(File, ID_FILE_NEW, "New\tCtrl+N"); File.AppendItem(self.File_New)
		self.File_NewFromTemplate = wx.Menu(); File.AppendMenu(wx.NewId(), "New From Template", self.File_NewFromTemplate)
		#self.File_NewFromTemplateEditTemplates = wx.MenuItem(File, ID_FILE_NEW_FROM_TEMPLATE_EDIT_TEMPLATES, "Edit Templates...\tCtrl+Alt+T"); self.File_NewFromTemplate.AppendItem(self.File_NewFromTemplateEditTemplates)
		self.File_Open = wx.MenuItem(File, ID_FILE_OPEN, "Open...\tCtrl+O"); File.AppendItem(self.File_Open)
		self.File_OpenRecent = wx.Menu(); File.AppendMenu(ID_FILE_OPEN_RECENT_CLEAR_MENU, "Open &Recent", self.File_OpenRecent)
		self.File_Save = wx.MenuItem(File, ID_FILE_SAVE, "&Save\tCtrl+S"); File.AppendItem(self.File_Save)
		self.File_SaveAs = wx.MenuItem(File, ID_FILE_SAVE_AS, "Save &As...\tCtrl+Shift+S"); File.AppendItem(self.File_SaveAs)
		self.File_SaveAll = wx.MenuItem(File, ID_FILE_SAVE_ALL, "Save All"); File.AppendItem(self.File_SaveAll)

		File.AppendSeparator()

		self.File_Close = wx.MenuItem(File, ID_FILE_CLOSE, "&Close\tCtrl+W"); File.AppendItem(self.File_Close) # File -> Close
		self.File_CloseAllTabs = wx.MenuItem(File, ID_FILE_CLOSE_ALL_TABS, "Close All Tabs"); File.AppendItem(self.File_CloseAllTabs) # File -> Close All Tabs

		File.AppendSeparator()
		self.File_NewProject = wx.MenuItem(File, ID_FILE_NEW_PROJECT, "New Project\tCtrl+Shift+N"); File.AppendItem(self.File_NewProject)
		#self.File_NewProjectFromTemplate = wx.Menu(); File.AppendMenu(wx.NewId(), "New Project From Template", self.File_NewProjectFromTemplate)
		#self.File_NewProjectFromTemplateEditTemplates = wx.MenuItem(File, ID_FILE_NEW_PROJECT_FROM_TEMPLATE_EDIT_TEMPLATES, "Edit Project Templates..."); self.File_NewProjectFromTemplate.AppendItem(self.File_NewProjectFromTemplateEditTemplates)
		# File.Append(ID_FILE_SAVE_PROJECT, "Save Project")
		# File.Append(ID_FILE_SAVE_PROJECT_AS, "Save Project As...")
		self.File_CloseProject = wx.MenuItem(File, ID_FILE_CLOSE_PROJECT, "Close Project"); File.AppendItem(self.File_CloseProject)
		File.AppendSeparator()
		self.File_Exit = wx.MenuItem(File, ID_FILE_EXIT, "E&xit"); File.AppendItem(self.File_Exit)
		self.menubar.Append(File, "&File")

		# Edit
		Edit = wx.Menu()
		self.Edit_Undo = wx.MenuItem(Edit, ID_EDIT_UNDO, "Undo\tCtrl+Z"); Edit.AppendItem(self.Edit_Undo)
		self.Edit_Redo = wx.MenuItem(Edit, ID_EDIT_REDO, "Redo\tCtrl+Y"); Edit.AppendItem(self.Edit_Redo)
		Edit.AppendSeparator()
		self.Edit_Cut = wx.MenuItem(Edit, ID_EDIT_CUT, "Cut\tCtrl+X"); Edit.AppendItem(self.Edit_Cut)
		self.Edit_Copy = wx.MenuItem(Edit, ID_EDIT_COPY, "Copy\tCtrl+C"); Edit.AppendItem(self.Edit_Copy)
		self.Edit_Paste = wx.MenuItem(Edit, ID_EDIT_PASTE, "Paste\tCtrl+V"); Edit.AppendItem(self.Edit_Paste)
		#ID_EDIT_PASTE_FROM_HISTORY # Wishlist item
		Edit.AppendSeparator()
		self.Edit_SelectAll = wx.MenuItem(Edit, ID_EDIT_SELECT_ALL, "Select All\tCtrl+A"); Edit.AppendItem(self.Edit_SelectAll)
		self.Edit_Mode = wx.Menu(); Edit.AppendMenu(wx.NewId(), "Mode", self.Edit_Mode)
		self.Edit_ModeInsert = wx.MenuItem(Edit, ID_EDIT_MODE_INSERT, "Insert"); self.Edit_Mode.AppendItem(self.Edit_ModeInsert)
		self.Edit_ModeOverwrite = wx.MenuItem(Edit, ID_EDIT_MODE_OVERWRITE, "Overwrite"); self.Edit_Mode.AppendItem(self.Edit_ModeOverwrite)
		#Edit_Select = wx.Menu()
		#Edit.AppendMenu(ID_EDIT_SELECT, "Select", Edit_Select)
		#Edit_Select.Append(ID_EDIT_SELECT_WORD, "Word")
		#Edit_Select.Append(ID_EDIT_SELECT_LINE, "Line")
		#Edit_Select.Append(ID_EDIT_SELECT_PARAGRAPH, "Paragraph")
		#Edit_Select.Append(ID_EDIT_SELECT_ENCLOSING_BRACKETS, "Enclosing Brackets")
		#Edit_Select.Append(ID_EDIT_SELECT_CURRENT_SCOPE, "Current Scope")
		#Edit_Select.Append(ID_EDIT_SELECT_ALL, "All")
		Edit.AppendSeparator()
		self.Edit_Define = wx.MenuItem(Edit, ID_EDIT_DEFINE, "Define Selection\tCtrl+Shift+D"); Edit.AppendItem(self.Edit_Define)
		Edit.AppendSeparator()
		self.Edit_Find = wx.Menu(); Edit.AppendMenu(wx.NewId(), "Find", self.Edit_Find)
		self.Edit_FindFind = wx.MenuItem(Edit, ID_EDIT_FIND, "Find...\tCtrl+F"); self.Edit_Find.AppendItem(self.Edit_FindFind)
		self.Edit_FindOnWeb = wx.MenuItem(Edit, ID_EDIT_FIND_ON_WEB, "Find on the Web...\tCtrl+Shift+F"); Edit.AppendItem(self.Edit_FindOnWeb)
		Edit.AppendSeparator()
		# Edit_Find.Append(ID_EDIT_FIND_IN_PROJECT, "Find in Project...\tCtrl+Shift+F")
		self.Edit_Find.AppendSeparator()
		self.Edit_FindNext = wx.MenuItem(Edit, ID_EDIT_FIND_NEXT, "Find Next\tF3"); self.Edit_Find.AppendItem(self.Edit_FindNext)
		self.Edit_FindPrevious = wx.MenuItem(Edit, ID_EDIT_FIND_PREVIOUS, "Find Previous\tShift+F3"); self.Edit_Find.AppendItem(self.Edit_FindPrevious)
		self.Edit_Find.AppendSeparator()
		self.Edit_FindReplaceAll = wx.MenuItem(Edit, ID_EDIT_FIND_REPLACE_ALL, "Replace All\tCtrl+H"); self.Edit_Find.AppendItem(self.Edit_FindReplaceAll)
		self.Edit_FindReplaceInSelection = wx.MenuItem(Edit, ID_EDIT_FIND_REPLACE_IN_SELECTION, "Replace in Selection"); self.Edit_Find.AppendItem(self.Edit_FindReplaceInSelection)
		self.Edit_Preferences = wx.MenuItem(Edit, ID_EDIT_PREFERENCES, "Preferences..."); Edit.AppendItem(self.Edit_Preferences)
		self.menubar.Append(Edit, "&Edit")

		# View
		View = wx.Menu()
		self.View_Project = wx.MenuItem(View, ID_VIEW_PROJECT, "Project\tF7"); View.AppendItem(self.View_Project)
		self.View_Output = wx.MenuItem(View, ID_VIEW_OUTPUT, "Output\tF8"); View.AppendItem(self.View_Output)
		View.AppendSeparator()
		self.View_Font = wx.Menu(); View.AppendMenu(wx.NewId(), "Font", self.View_Font)
		self.View_FontBigger = wx.MenuItem(View, ID_VIEW_FONT_BIGGER, "Bigger"); self.View_Font.AppendItem(self.View_FontBigger)
		self.View_FontSmaller = wx.MenuItem(View, ID_VIEW_FONT_SMALLER, "Smaller"); self.View_Font.AppendItem(self.View_FontSmaller)
		self.View_Gutter = wx.Menu(); View.AppendMenu(wx.NewId(), "Gutter", self.View_Gutter)
		self.View_GutterFoldings = wx.MenuItem(View, ID_VIEW_GUTTER_FOLDINGS, "Foldings"); self.View_Gutter.AppendItem(self.View_GutterFoldings)
		self.View_GutterLineNumbers = wx.MenuItem(View, ID_VIEW_GUTTER_LINE_NUMBERS, "Line Numbers"); self.View_Gutter.AppendItem(self.View_GutterLineNumbers)
		self.View_Theme = wx.Menu(); View.AppendMenu(wx.NewId(), "Theme", self.View_Theme)
		View.AppendSeparator()
		self.View_SoftWrap = wx.MenuItem(View, ID_VIEW_SOFT_WRAP, "Soft Wrap"); View.AppendItem(self.View_SoftWrap)
		self.View_WrapColumn = wx.Menu(); View.AppendMenu(wx.NewId(), "Wrap Coumn", self.View_WrapColumn)
		self.View_WrapColumnShowIndicator = wx.MenuItem(View, ID_VIEW_WRAP_COLUMN_SHOW_INDICATOR, "Show Indicator"); self.View_WrapColumn.AppendItem(self.View_WrapColumnShowIndicator)
		self.View_WrapColumn.AppendSeparator()
		self.View_WrapColumn40 = wx.MenuItem(View, ID_VIEW_WRAP_COLUMN_40, "40"); self.View_WrapColumn.AppendItem(self.View_WrapColumn40)
		self.View_WrapColumn78 = wx.MenuItem(View, ID_VIEW_WRAP_COLUMN_78, "78"); self.View_WrapColumn.AppendItem(self.View_WrapColumn78)
		self.View_WrapColumn.AppendSeparator()
		self.View_WrapColumnOther = wx.MenuItem(View, ID_VIEW_WRAP_COLUMN_OTHER, "Other..."); self.View_WrapColumn.AppendItem(self.View_WrapColumnOther)
		self.ViewLineEndings = wx.MenuItem(View, ID_VIEW_LINE_ENDINGS, "Line Endings\tCtrl+Shift+9"); View.AppendItem(self.ViewLineEndings)
		self.ViewInvisibles = wx.MenuItem(View, ID_VIEW_SHOW_INVISIBLES, "Show Invisibles\tCtrl+Shift+8"); View.AppendItem(self.ViewInvisibles)
		View.AppendSeparator()
		self.ViewFoldAll = wx.MenuItem(View, ID_VIEW_FOLD_ALL, "Fold All\tCtrl+Shift+L"); View.AppendItem(self.ViewFoldAll)
		self.menubar.Append(View, "&View")

		# Tools
		#Tools = wx.Menu()
		#self.Tools_Go = wx.MenuItem(Tools, ID_TOOLS_GO, "Go\tF5"); Tools.AppendItem(self.Tools_Go)
		#self.menubar.Append(Tools, "&Tools")

		# Navigation
		Navigation = wx.Menu()
		self.Navigation_NextFileTab = wx.MenuItem(Navigation, ID_NAVIGATE_NEXT_FILE_TAB, "Next File Tab\tCtrl+Tab"); Navigation.AppendItem(self.Navigation_NextFileTab)
		self.Navigation_PreviousFileTab = wx.MenuItem(Navigation, ID_NAVIGATE_PREVIOUS_FILE_TAB, "Previous File Tab\tCtrl+Shift+Tab"); Navigation.AppendItem(self.Navigation_PreviousFileTab)
		self.Navigation_GoToLine = wx.MenuItem(Navigation, ID_NAVIGATE_GO_TO_LINE, "Go to Line...\tCtrl+G"); Navigation.AppendItem(self.Navigation_GoToLine)
		#self.Navigation_OpenSelectedFile = wx.MenuItem(Navigation, ID_NAVIGATE_OPEN_SELECTED_FILE, "Open Selected File\tCtrl+Shift+O"); Navigation.AppendItem(self.Navigation_OpenSelectedFile)
		self.menubar.Append(Navigation, "&Navigation")

		# Help
		Help = wx.Menu()
		self.Help_muHelp = wx.MenuItem(Help, ID_HELP_MU_HELP, "&mu Help\tF1"); Help.AppendItem(self.Help_muHelp)
		self.HelpAboutMu = wx.MenuItem(Help, ID_HELP_ABOUT, "&About mu"); Help.AppendItem(self.HelpAboutMu)

		self.menubar.Append(Help, "&Help")
		self.SetMenuBar(self.menubar)

		# ================================================================
		# Menu Bindings
		# ================================================================

		# File
		wx.EVT_MENU(self, ID_FILE_NEW, self.onFileNew)
		#wx.EVT_MENU(self, ID_FILE_NEW_FROM_TEMPLATE_EDIT_TEMPLATES, self.onFileNewFromTemplateEditTemplates)
		wx.EVT_MENU(self, ID_FILE_OPEN, self.onFileOpen)
		wx.EVT_MENU(self, ID_FILE_CLOSE, self.onFileClose)
		wx.EVT_MENU(self, ID_FILE_CLOSE_ALL_TABS, self.onFileCloseAllTabs)
		wx.EVT_MENU(self, ID_FILE_SAVE, self.onFileSave)
		wx.EVT_MENU(self, ID_FILE_SAVE_AS, self.onFileSaveAs)
		wx.EVT_MENU(self, ID_FILE_SAVE_ALL, self.onFileSaveAll)
		wx.EVT_MENU(self, ID_FILE_NEW_PROJECT, self.onFileNewProject)
		#wx.EVT_MENU(self, ID_FILE_NEW_PROJECT_FROM_TEMPLATE_EDIT_TEMPLATES, self.onFileNewProjectFromTemplateEditTemplates)
		# wx.EVT_MENU(self, ID_FILE_SAVE_PROJECT, self.onFileSaveProject)
		# wx.EVT_MENU(self, ID_FILE_SAVE_PROJECT_AS, self.onFileSaveProjectAs)
		wx.EVT_MENU(self, ID_FILE_CLOSE_PROJECT, self.onFileCloseProject)
		wx.EVT_MENU(self, ID_FILE_EXIT, self.onFileExit)

		# Edit
		wx.EVT_MENU(self, ID_EDIT_UNDO, self.onEditUndo)
		wx.EVT_MENU(self, ID_EDIT_REDO, self.onEditRedo)
		wx.EVT_MENU(self, ID_EDIT_CUT, self.onEditCut)
		wx.EVT_MENU(self, ID_EDIT_COPY, self.onEditCopy)
		wx.EVT_MENU(self, ID_EDIT_PASTE, self.onEditPaste)
		wx.EVT_MENU(self, ID_EDIT_SELECT_ALL, self.onEditSelectAll)
		wx.EVT_MENU(self, ID_EDIT_MODE_INSERT, self.onEditModeInsert)
		wx.EVT_MENU(self, ID_EDIT_MODE_OVERWRITE, self.onEditModeOverwrite)
		wx.EVT_MENU(self, ID_EDIT_DEFINE, self.onEditDefine)
		wx.EVT_MENU(self, ID_EDIT_FIND, self.onEditFind)
		# wx.EVT_MENU(self, ID_EDIT_FIND_IN_PROJECT, self.onEditFindInProject)
		wx.EVT_MENU(self, ID_EDIT_FIND_NEXT, self.onFind)
		wx.EVT_MENU(self, ID_EDIT_FIND_ON_WEB, self.onFindOnWeb)
		wx.EVT_MENU(self, ID_EDIT_FIND_PREVIOUS, self.onEditFindPrevious)
		wx.EVT_MENU(self, ID_EDIT_FIND_REPLACE_ALL, self.onEditFindReplaceAll)
		wx.EVT_MENU(self, ID_EDIT_FIND_REPLACE_IN_SELECTION, self.onEditFindReplaceInSelection)
		wx.EVT_MENU(self, ID_EDIT_PREFERENCES, self.onEditPreferences)

		# View
		wx.EVT_MENU(self, ID_VIEW_PROJECT, self.onViewProject)
		wx.EVT_MENU(self, ID_VIEW_OUTPUT, self.onViewOutput)
		wx.EVT_MENU(self, ID_VIEW_FONT_BIGGER, self.onViewFontBigger)
		wx.EVT_MENU(self, ID_VIEW_FONT_SMALLER, self.onViewFontSmaller)
		wx.EVT_MENU(self, ID_VIEW_GUTTER_FOLDINGS, self.onViewGutterFolding)
		wx.EVT_MENU(self, ID_VIEW_GUTTER_LINE_NUMBERS, self.onViewLineNumbers)
		wx.EVT_MENU(self, ID_VIEW_SOFT_WRAP, self.onViewSoftWrap)
		wx.EVT_MENU(self, ID_VIEW_WRAP_COLUMN_SHOW_INDICATOR, self.onViewWrapColumnShowIndicator)
		wx.EVT_MENU(self, ID_VIEW_WRAP_COLUMN_40, self.onViewWrapColumnSetColumn40)
		wx.EVT_MENU(self, ID_VIEW_WRAP_COLUMN_78, self.onViewWrapColumnSetColumn78)
		wx.EVT_MENU(self, ID_VIEW_WRAP_COLUMN_OTHER, self.onViewWrapColumnSetColumnOther)
		wx.EVT_MENU(self, ID_VIEW_SHOW_INVISIBLES, self.onShowInvisibles)
		wx.EVT_MENU(self, ID_VIEW_FOLD_ALL, self.onViewFoldAll)
		wx.EVT_MENU(self, ID_VIEW_LINE_ENDINGS, self.onViewLineEndings)
		wx.EVT_MENU(self, ID_VIEW_SOFT_WRAP, self.onViewSoftWrap)

		# Tools
		#wx.EVT_MENU(self, ID_TOOLS_GO, self.onToolsGo)

		# Navigation
		wx.EVT_MENU(self, ID_NAVIGATE_NEXT_FILE_TAB, self.onNavigateNextFileTab)
		wx.EVT_MENU(self, ID_NAVIGATE_PREVIOUS_FILE_TAB, self.onNavigatePreviousFileTab)
		wx.EVT_MENU(self, ID_NAVIGATE_GO_TO_LINE, self.onNavigateGoToLine)
		#wx.EVT_MENU(self, ID_NAVIGATE_OPEN_SELECTED_FILE, self.onNavigateSelectedFile)

		# Help
		wx.EVT_MENU(self, ID_HELP_MU_HELP, self.onHelpMuHelp)
		wx.EVT_MENU(self, wx.ID_ABOUT, self.onHelpAbout)


		# ===============================================================
		# Setup the toolbar
		# ===============================================================
		toolbar = wx.ToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize, wx.TB_FLAT | wx.TB_NODIVIDER)
		toolbar.SetToolBitmapSize(wx.Size(16,16))
		#toolbar_bmp1 = wx.ArtProvider_GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, wx.Size(16, 16))

		#toolbar_savebmp = wx.ArtProvider_GetBitmap(wx.wx.ART_HELP_PAGE, wx.ART_OTHER, wx.Size(16,16))

		toolbar_new_image = wx.ArtProvider_GetBitmap(wx.wx.ART_NORMAL_FILE, wx.ART_OTHER, wx.Size(16,16))
		toolbar_open_image = wx.ArtProvider_GetBitmap(wx.wx.ART_FILE_OPEN, wx.ART_OTHER, wx.Size(16,16))
		toolbar_save_image = wx.ArtProvider_GetBitmap(wx.wx.ART_FILE_SAVE, wx.ART_OTHER, wx.Size(16,16))
		toolbar_info_image = wx.ArtProvider_GetBitmap(wx.wx.ART_QUESTION, wx.ART_OTHER, wx.Size(16,16))

		toolbar.AddLabelTool(ID_FILE_NEW, "New", toolbar_new_image)
		toolbar.AddLabelTool(ID_FILE_OPEN, "Open", toolbar_open_image)
		toolbar.AddLabelTool(ID_FILE_SAVE, "Save", toolbar_save_image)
		toolbar.AddSeparator()
		toolbar.AddLabelTool(ID_EDIT_FIND_ON_WEB, "Info", toolbar_info_image)


		'''
		tb4.AddSeparator()
		tb4.AddLabelTool(101, "Item 4", tb4_bmp1)
		tb4.AddLabelTool(101, "Item 5", tb4_bmp1)
		tb4.AddLabelTool(101, "Item 6", tb4_bmp1)
		tb4.AddLabelTool(101, "Item 7", tb4_bmp1)
		tb4.AddLabelTool(101, "Item 8", tb4_bmp1)
		'''
		toolbar.Realize()


		#self.Bind(wx.EVT_TOOL, self.ontoolbarclick, id=123)



		'''
		wx.ART_ADD_BOOKMARK
		wx.ART_DEL_BOOKMARK
		wx.ART_HELP_SIDE_PANEL
		wx.ART_HELP_SETTINGS
		wx.ART_HELP_BOOK
		wx.ART_HELP_FOLDER
		wx.ART_HELP_PAGE
		wx.ART_GO_BACK
		wx.ART_GO_FORWARD
		wx.ART_GO_UP
		wx.ART_GO_DOWN
		wx.ART_GO_TO_PARENT
		wx.ART_GO_HOME
		wx.ART_FILE_OPEN
		wx.ART_FILE_SAVE
		wx.ART_FILE_SAVE_AS
		wx.ART_PRINT
		wx.ART_HELP
		wx.ART_TIP
		wx.ART_REPORT_VIEW
		wx.ART_LIST_VIEW
		wx.ART_NEW_DIR
		wx.ART_HARDDISK
		wx.ART_FLOPPY
		wx.ART_CDROM
		wx.ART_REMOVABLE
		wx.ART_FOLDER
		wx.ART_FOLDER_OPEN
		wx.ART_GO_DIR_UP
		wx.ART_EXECUTABLE_FILE
		wx.ART_NORMAL_FILE
		wx.ART_TICK_MARK
		wx.ART_CROSS_MARK
		wx.ART_ERROR
		wx.ART_QUESTION
		wx.ART_WARNING
		wx.ART_INFORMATION
		wx.ART_MISSING_IMAGE
		wx.ART_COPY
		wx.ART_CUT
		wx.ART_PASTE
		wx.ART_DELETE
		wx.ART_NEW
		wx.ART_UNDO
		wx.ART_REDO
		wx.ART_QUIT
		wx.ART_FIND
		wx.ART_FIND_AND_REPLACE
		'''



		# Make sure menus are on if necessary.
		self.switchMenus()

		# Load Recent Menu Items
		self.LoadRecentItems()

		# Load up themes menu
		self.LoadThemesMenu()

		# Load file templates
		self.LoadFileTemplates()


		# ===============================================================
		# Panes
		# ===============================================================
		# A find panel at the top for incremental searching?
		# I like this idea when going Ctrl+F, i really like how safari 
		# does this and keeps the dialog boxes off the screen.
		# self._mgr.AddPane(self.InitContentPane(), wx.aui.AuiPaneInfo()
		# .Name("QuickSearch").Caption("").Top().CloseButton(True)
		# .MaximizeButton(False))
		#self._mgr.GetPane("QuickSearch").Hide()
		# add the toolbars to the manager

		# Add Toolbar
		self._mgr.AddPane(toolbar, wx.aui.AuiPaneInfo().
						  Name("toolbar").Caption("Shortcuts").
						  ToolbarPane().Top().Row(1).
						  LeftDockable(False).RightDockable(False))


		# Project Pane
		self._mgr.AddPane(self.CreateTreeCtrl(), 
		  wx.aui.AuiPaneInfo()
		  .Name("ProjectPane")
		  .Caption("Project")
		  .BestSize(wx.Size(200,100))
		  .Left()
		  .Layer(1)
		  .Position(1)
		  .CloseButton(True)
		  .MaximizeButton(False))
		self._mgr.GetPane("ProjectPane").Hide().Left().Layer(0).Row(0).Position(0)

		# Output Pane
		self._mgr.AddPane(self.CreateStyledEditor(), 
		  wx.aui.AuiPaneInfo()
		  .Name("OutputPane")
		  .Caption("Output")
		  .Bottom()
		  .Layer(1)
		  .Position(1)
		  .CloseButton(True)
		  .MaximizeButton(False))
		self._mgr.GetPane("OutputPane").Hide().Bottom().Layer(0).Row(0).Position(0) 

		# Content Pane
		self._mgr.AddPane(self.InitContentPane(), 
		  wx.aui.AuiPaneInfo()
		  .Name("ContentPane")
		  .CenterPane())
		self._mgr.GetPane("ContentPane").Show() # Shows Main Pane

		# Preferences
		self._mgr.AddPane(SettingsPanel(self, self), 
		  wx.aui.AuiPaneInfo()
		  .Name("settings")
		  .Caption("Preferences")
		  .Dockable(False)
		  .Float()
		  .CloseButton(True)
		  .MaximizeButton(False)
		  .Hide())

		self._mgr.SetFlags(self._mgr.GetFlags() ^ wx.aui.AUI_MGR_TRANSPARENT_DRAG)
		self._mgr.Update()

		# ================================================================
		# Status Bar
		# ================================================================
		self.StatusBar = self.CreateStatusBar(4, wx.ST_SIZEGRIP)
		#self.StatusBar.SetStatusWidths([-3, -2, -1, -3])
		#self.StatusBar.SetStatusText(" ", 0) # Line: N Column: N
		#self.StatusBar.SetStatusText(" ", 1) # {File Type}
		#self.StatusBar.SetStatusText(" ", 2) # Tab Size: 000
		#self.StatusBar.SetStatusText(" ", 3) # {Symbol}

		# ================================================================
		# Event Bindings
		# Methods to handle common events
		# ================================================================

		# Notebook
		self.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.onPageClosed) 
		self.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.onPageChanged)

		# Find Dialog
		self.Bind(wx.EVT_FIND, self.onFind)
		self.Bind(wx.EVT_FIND_NEXT, self.onFind)
		self.Bind(wx.EVT_COMMAND_FIND_REPLACE, self.onFind)
		self.Bind(wx.EVT_COMMAND_FIND_REPLACE_ALL, self.onFind)
		self.Bind(wx.EVT_FIND_CLOSE, self.onEditFindClose)

		# Window
		self.Bind(wx.EVT_CLOSE, self.OnMuClose)

		# Load the last theme selected from the configuration file.
		themeDirectory = self.configuration["Paths"]["Themes"]
		theme = self.configuration["UI"]["Theme"]

		# Load files from command line or add blank tab.
		if len(sys.argv) > 1:
			for arg in sys.argv:
				if arg.find("mu.py") == -1:
					#print os.split(arg)[1]
					self.AddTab(arg)
		else:
			print 'nothing passed in'
			# Add a blank tab to start working in.
			self.AddTab()

		self.ActiveEditor().SetTheme(themeDirectory + "/" + theme, self.GetCurrentFileExtension())



	def ontoolbarclick(self):
		print('adf')


	def respawn(self, event):
		filePath = self.filesOpen[self.ContentPane.GetSelection()][0]
		tabName = self.ContentPane.GetPageText(self.ContentPane.GetSelection())
		fileExtension = so.path.splitext(tabName)[1]

		if fileExtension == ".py":
			logging.info("running " + chr(13) + filePath + chr(13))	

	def OnMuClose(self, event):
		# Close all open tabs, prompting to save any that are dirty.
		self.onFileCloseAllTabs(event) 

		# Save current window dimensions and location to configuration before closing

		# Save Dimensions
		Width, Height = self.GetSize()
		self.configuration["UI"]["Width"] = Width
		self.configuration["UI"]["Height"] = Height

		# Save Location
		FinalX, FinalY = self.GetPosition()
		self.configuration["UI"]["InitX"] = FinalX
		self.configuration["UI"]["InitY"] = FinalY

		self.saveConfiguration()
		self.Destroy()

	# ================================================================
	# Recent Items Methods
	# ================================================================
	def LoadRecentItems(self):
	# Iterate over all items in the plist file and remove them 
	# (if they exist) from the menu (but not the plist file)
		self.loadConfiguration() # load config from plist
		RecentFiles = self.configuration["Recent Files"]
		if len(RecentFiles) > 0:
			# Remove all items that are in the list
			# This should honestly just delete everything out of 
			for file in RecentFiles:
				try: 
					# remove item from the menu
					self.File_OpenRecent.Remove(self.File_OpenRecent.FindItem(file)) 
				except: pass # This should rarely happen, but just in case.

			# Now add them all back in again
			for file in self.configuration["Recent Files"]:
				item = self.File_OpenRecent.Prepend(-1, file)
				self.Bind(wx.EVT_MENU, self.OnNewItemSelected, item)

			# If the clear menu doesnt exist, add it and a separator
			# This prevents duplicated clear item options.
			clearPos = self.File_OpenRecent.FindItem("Clear Recent Items")
			if clearPos == -1:
				self.File_OpenRecent.AppendSeparator()
				self.ClearRecentItemsMenu = self.File_OpenRecent.Append(wx.NewId(), "Clear Recent Items")
				self.Bind(wx.EVT_MENU, self.OnNewItemSelected, self.ClearRecentItemsMenu)
			self.menubar.Enable(ID_FILE_OPEN_RECENT_CLEAR_MENU, True) # Enable Recent Items
		else:
			self.menubar.Enable(ID_FILE_OPEN_RECENT_CLEAR_MENU, False) # Disable Recent Items

	def AddToRecentItems(self, filePath):
		# If file already in the list do not add again
		itemPos = self.File_OpenRecent.FindItem(filePath)
		if itemPos == -1:
			self.loadConfiguration()
			self.configuration["Recent Files"].append(filePath)
			self.saveConfiguration()
			self.LoadRecentItems()
		else: # Item already exists
			General.info("AddToRecentItems() - Item already exists at " + filePath)

	# This needs more testing
	def RemoveFromRecentItems(self, event):
		# remove item from recent items list by name.
		# For when a file is deleted from the system, then someone attempts to 
		# open it from the recent items list.
		item = self.GetMenuBar().FindItemById(event.GetId())
		filePath = item.GetText()
		self.configuration["Recent Files"].remove(filePath)
		self.saveConfiguration()
		self.LoadRecentItems()

	def ClearRecentItems(self):
		# Deletes them from the file and calls loadRecentItems.

		# Remove all items that are in the list *do this now, before deleting them from the config.
		for file in self.configuration["Recent Files"]:
			try: self.File_OpenRecent.Remove(self.File_OpenRecent.FindItem(file)) #remove item from the menu
			except: General.info("ClearRecentItems()")

		del self.configuration["Recent Files"][:] # Remove recent files from configuration
		self.saveConfiguration()
		self.LoadRecentItems()

	def OnNewItemSelected(self, event):
		item = self.GetMenuBar().FindItemById(event.GetId())
		fileName = item.GetText()
		if fileName == "Clear Recent Items":
			self.ClearRecentItems() # Call method to remove all recent items.
		else:
			# Launch recently viewed item
			try:
				# if this is a .muproject we pass off loading to that, otherwise call AddTab
				if os.path.splitext(fileName)[1] == ".muproject":
					self.loadProject(fileName)
				else:
					self.AddTab(fileName)
			except: 
				# File was probably deleted; in 
				# which case it should be removed from the list.
				message = "The file could not be opened. Maybe its been deleted?"
				d = wx.MessageDialog(self, message, "Couldn't open file", wx.OK | wx.ICON_INFORMATION)
				d.ShowModal()
				d.Destroy()
				# Remove the lost item from the recent items list
				self.RemoveFromRecentItems(event)
				General.warning("Exception adding tab. Tab name = " + str(fileName))
				if len(self.filesOpen) == 0:
					self.AddTab()

	# ====================================================================
	# Load file templates from the filesystem.
	# ====================================================================

	def LoadFileTemplates(self):
		self.FileTemplates.clear()
		self.mirrorFileSystemInMenu(self.File_NewFromTemplate, "Templates/File")

	def mirrorFileSystemInMenu(self, parentMenu, path):
		for cdir, subdirs, files in os.walk(path):
			if cdir <> "." and cdir.find(".svn") == -1:
				# This helps us avoid adding 
				# every directory at every level.
				if cdir == path:
					# If this is the root we want to append this item 
					# to the main item, we just want to start
					# listing its contents under parentMenu
					## print cdir
					## print path
					# Recurse through top level
					# TODO: See if i can get a count of child menu items instead of doing this
					# If count(menuItems under parent) = 0: this is the root iteration.
					if cdir == "Templates/File":
					#if cdir.replace(path, "") == "":
						# load files in root
						for file in files: parentMenu.Append(-1, file) # not binding to menu here, should we in event of top level files being added to the menu?
						# now recurse
						for subdir in subdirs: self.mirrorFileSystemInMenu(parentMenu, os.path.join(cdir, subdir))
					else:
						# if this is the top level, we use parentMenu instead of submenu below
						submenu = wx.Menu()
						submenuID = wx.NewId()
						submenuCaption = os.path.split(cdir)[1] # directory name
						parentMenu.AppendMenu(submenuID, submenuCaption, submenu)
						# load files
						for file in files:
							id = wx.NewId()
							item = submenu.Append(id, file)
							self.Bind(wx.EVT_MENU, self.LoadTemplate, item)
							self.FileTemplates[id] = cdir + "/" + file
							#print("add " + cdir + "/" + file)

						# now recurse
						for subdir in subdirs: self.mirrorFileSystemInMenu(submenu, os.path.join(cdir, subdir))

	def LoadTemplate(self, event):
		#item = self.GetMenuBar().FindItemById(event.GetId())
		templatePath = self.FileTemplates[event.GetId()]
		file = open(templatePath, "r")
		fileData = file.read()
		file.close()
		self.AddTab()
		self.ActiveEditor().SetText(fileData)



	def dynamicMenuCallback(self):
		print 'called dynamic menu'

	# Update configuration file
	def saveConfiguration(self):
		plistlib.writePlist(self.configuration, "mu.configure") 

	# Reload the configuration file
	def loadConfiguration(self):
		self.configuration = plistlib.readPlist("mu.configure")

	# Shortcut to reach the editor control on the active notebook panel.
	def ActiveEditor(self):
		return self.ContentPane.GetPage(self.ContentPane.GetSelection())

	def UpdateStatusBar(self):
		# If there are no pages open clear everything
		if len(self.filesOpen) > 0:

			# Cell 1 - Position (Line: XXX Column XXX)
			position = \
			"Line:      " + str(self.ActiveEditor().GetCurrentLine()+1) + "   " + \
			"Column:      " + str(self.ActiveEditor().GetColumn(self.ActiveEditor().GetCurrentPos())+1) + "   " + \
			"of " + str(self.ActiveEditor().GetLineCount())

			self.StatusBar.SetStatusText(position, 0) # Line: XXX  Column: XXX

			# Cell 2 - File Type
			self.StatusBar.SetStatusText(self.GetFileTypeOfCurrentDocument(), 1)

			# Cell 3 Indent and Mode
			indent = str(self.ActiveEditor().GetIndent())
			mode = "INS"
			if self.ActiveEditor().GetOvertype() == True: mode = "OVR"

			self.StatusBar.SetStatusText(mode + "   Indent: " + indent, 2)

			# Currently Selected Range
			# Selection Details: http://www.yellowbrain.com/stc/selnpos.html
			# This is temporary so i can try to work out column replace in my head.
			anchor = self.ActiveEditor().GetAnchor()
			start = self.ActiveEditor().GetSelectionStart()
			end = self.ActiveEditor().GetSelectionEnd()
			rect = self.ActiveEditor().SelectionIsRectangle()
			self.StatusBar.SetStatusText("anchor: " + str(anchor) + " start: " + str(start) + " end: " + str(end) + " rect: " + str(rect), 3)


		else:
			self.StatusBar.SetStatusText("")
			self.StatusBar.SetStatusText(" ", 3) # {Mode}

	def GetCurrentFileExtension(self):
		currentFileName = self.ContentPane.GetPageText(self.ContentPane.GetSelection())
		currentFilesExtension = os.path.splitext(currentFileName)[1]
		return currentFilesExtension

	def GetFileTypeOfCurrentDocument(self):
		currentFileName = self.ContentPane.GetPageText(self.ContentPane.GetSelection())
		currentFilesExtension = os.path.splitext(currentFileName)[1]
		currentFileType = self.GetFileTypeByExtension(currentFilesExtension)
		return currentFileType

	# I pieced this together from the list of supported file types
	# for Scintilla on Wikipedia, it may be out of date even as I type this
	# but covers way more than I currently care about.
	# Maybe this should move over to editor.py, along with 
	# some internal information about the document.
	def GetFileTypeByExtension(self, fileExtension):
	  fileExtension = fileExtension.lower()
	  filetypes = {
				  ".ada"	: "Ada"
				, ".asm" 	: "Assembler"
				, ".bat" 	: "Batch"
				, ".c" 		: "C"
				, ".cpp" 	: "C++"
				, ".cxx" 	: "C++"
				, ".c++" 	: "C++"
				, ".cs" 	: "C#"
				, ".css" 	: "CSS"
				, ".d" 		: "D"
				, ".diff" 	: "Difference"
				, ".err" 	: "Errorlist"
				, ".f" 		: "Fortran"
				, ".f77" 	: "Fortran"
				, ".f90" 	: "Fortran"
				, ".h" 		: "Header"
				, ".hpp" 	: "Header"
				, ".html" 	: "Hypertext"
				, ".htm" 	: "Fortran"
				, ".java" 	: "Java"
				, ".js" 	: "JavaScript"
				, ".tex" 	: "TeX"
				, ".lisp" 	: "Lisp"
				, ".lua" 	: "Lua"
				, ".mat" 	: "MatLab"
				, ".make" 	: "Makefile"
				, ".p" 		: "Pascal"
				, ".pl" 	: "Perl"
				, ".php" 	: "PHP"
				, ".py" 	: "Python"
				, ".pyw" 	: "Python"
				, ".rb" 	: "Ruby"
				, ".sh" 	: "Shell"
				, ".sql" 	: "SQL"
				, ".tcl" 	: "TCL"
				, ".txt" 	: "Text"
				, ".vb" 	: "VB"
				, ".vbs" 	: "VBScript"
				, ".xml" 	: "XML"
				, ".yaml" 	: "YAML",
				}

	  # Identify all unrecognized file types as Text
	  if not filetypes.has_key(fileExtension): filetype = "Text"
	  else: filetype = filetypes[fileExtension]

	  return filetype

	# This method could be used to dispatch lots of good stuff.
	def OnUpdateUI(self, event):
		# Perform Brace Highlighting
		braceAtCaret = -1
		braceOpposite = -1
		charBefore = None
		caretPos = self.ActiveEditor().GetCurrentPos()
		if caretPos > 0:
			charBefore = self.ActiveEditor().GetCharAt(caretPos - 1)
			styleBefore = self.ActiveEditor().GetStyleAt(caretPos - 1)
		# check before
		if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
			braceAtCaret = caretPos - 1
		# check after
		if braceAtCaret < 0:
			charAfter = self.ActiveEditor().GetCharAt(caretPos)
			styleAfter = self.ActiveEditor().GetStyleAt(caretPos)
			if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
					braceAtCaret = caretPos
		if braceAtCaret >= 0:
			braceOpposite = self.ActiveEditor().BraceMatch(braceAtCaret)
		if braceAtCaret != -1  and braceOpposite == -1:
			self.ActiveEditor().BraceBadLight(braceAtCaret)
		else:
			self.ActiveEditor().BraceHighlight(braceAtCaret, braceOpposite)

		# Updates the line and column numbers when you switch to different tabs.
		self.UpdateStatusBar()

	def onEditorKeyDown(self, event):
		#logging.info("onEditorKeyDown(), KeyCode = " + str(event.GetKeyCode()) )

		# Set dirty flag, probably not the best way
		keycode = event.GetKeyCode()
		# 27 = Esape Key
		# 370 = Enter Key
		# Keycodes created from right hand keypad fall outside the normal range.
		#  324, 325, 326, 327, 328, 329, 330, 331, 332, 333

		if event.GetKeyCode() < 256 or (event.GetKeyCode() >= 324 and event.GetKeyCode() <= 333) or event.GetKeyCode() == 370: #370 = Enter Key

			self.filesOpen[self.ContentPane.GetSelection()][1] = 1

			# if dirty and tab name doesnt contain a *, add one
			# do i really have to check the tabname every time?
			tabName = self.ContentPane.GetPageText(self.ContentPane.GetSelection())
			if tabName.find("*") == -1:
				self.ContentPane.SetPageText(self.ContentPane.GetSelection(), tabName + " *")
				self.SetTitle(self.ContentPane.GetPageText(self.ContentPane.GetSelection()) + " - mu " + self.version)
		event.Skip()

	# Convenience Methods
	def GetCurrentTabName(self):
		return self.ContentPane.GetPageText(self.ContentPane.GetSelection())

	def GetCurrentTabIndex(self):
		return self.ContentPane.GetSelection()

	# Handle Themes (loading menu and switching to)
	def LoadThemesMenu(self):
		themesPath = "./Themes"  # Insert path to  directory of interest here.
		dirList = os.listdir(themesPath)
		for fname in dirList:
			if fname.endswith(".tmTheme"):
				item = self.View_Theme.Append(-1, fname[:-len(".tmTheme")])
				self.Bind(wx.EVT_MENU, self.OnThemeSelected, item)

	def OnThemeSelected(self, event):
		item = self.GetMenuBar().FindItemById(event.GetId())
		text = item.GetText()

		themeDirectory = self.configuration["Paths"]["Themes"]
		self.ActiveEditor().SetTheme(themeDirectory + "/" + text + ".tmTheme", self.GetCurrentFileExtension())

		# Save selected theme back into the configuration
		self.configuration["UI"]["Theme"] = text + ".tmTheme"
		self.saveConfiguration()		

	# File Menu Events
	def onFileNew(self, event):
		logging.info("onFileNew(...)")
		self.AddTab()

	def onFileNewFromTemplateEditTemplates(self, event):
		logging.info("onFileNewFromTemplateEditTemplates()")

	# Handle opening files and loading projects 
	# if the file is a .muproject project file
	def onFileOpen(self, event):
		logging.info("onFileOpen(...)")
		dlg = wx.FileDialog(self, "Open", ".", "", "*.*", wx.OPEN)
		if dlg.ShowModal() == wx.ID_OK:
			if os.path.splitext(dlg.GetFilename())[1] == ".muproject":
				self.loadProject(dlg.GetPath())
			else:
				self.AddTab(dlg.GetPath())
		dlg.Destroy()

	def onFileClose(self, event):
		logging.info("onFileClose()")
		self.VerifyNotDirtyThenSaveOrClose()

	''' I was drinking when I named this next function... I still am. '''
	def VerifyNotDirtyThenSaveOrClose(self):
		logging.info("VerifyNotDirtyThenSaveOrClose()")
		closeTabAtIndex = self.ContentPane.GetSelection()
		tabName = self.ContentPane.GetPageText(self.ContentPane.GetSelection())
		if tabName.find("*") <> -1:
			# messagebox asking if you want to save first
			d = wx.MessageDialog(self, "Save changes to " + tabName + "?", "mu", wx.YES | wx.NO | wx.CANCEL | wx.ICON_QUESTION)
			answer = d.ShowModal()
			if answer == wx.ID_YES:
				self.onFileSaveAs(self) # todo, verify save before popping file out of buffer
				self.filesOpen.pop(closeTabAtIndex)
				self.ContentPane.DeletePage(closeTabAtIndex)
			if answer == wx.ID_NO:
				self.filesOpen.pop(closeTabAtIndex)
				self.ContentPane.DeletePage(closeTabAtIndex)
			if answer == wx.CANCEL:
				pass
			d.Destroy()
		else:
			self.filesOpen.pop(closeTabAtIndex)
			self.ContentPane.DeletePage(closeTabAtIndex)

		if self.ContentPane.GetPageCount() == 0:
			self.Title = "mu"

		self.switchMenus()
		self.UpdateStatusBar()

		#i like the way SciTE opens a new doc when youve closed them 
		# all so lets do that here too...
		if len(self.filesOpen) == 0: self.AddTab()

	def onPageClosed(self, event):
		self.VerifyNotDirtyThenSaveOrClose()
		event.Veto()

	# I can probably use this to refresh page 
	# title, ui, and other bullshit.
	def onPageChanged(self, event):
		logging.info("PageChanged()")
		CurrentPageText = self.ContentPane.GetPageText(self.ContentPane.GetSelection())
		if CurrentPageText <> "":
			# We've already calculated dirty for tab name, just copy it
			self.SetTitle(CurrentPageText + " - mu " + self.version)
			self.UpdateStatusBar()
		event.Veto()

		# Theme it based on current theme
		themeDirectory = self.configuration["Paths"]["Themes"]
		theme = self.configuration["UI"]["Theme"]
		self.ActiveEditor().SetTheme(themeDirectory + "/" + theme, self.GetCurrentFileExtension())

	# Close All Tabs
	# Verify each is clean before closing or prompt.
	def onFileCloseAllTabs(self, event):
		for i in range(0,len(self.filesOpen)):
			self.ContentPane.SetSelection(i)
			self.VerifyNotDirtyThenSaveOrClose()

	# Save
	def onFileSave(self, event):
		filePath = str(self.filesOpen[self.ContentPane.GetSelection()][0])
		fileText = self.ActiveEditor().GetText()
		print filePath
		if filePath <> "":
			self.SaveFile(filePath, fileText)
			tabName = self.ContentPane.GetPageText(self.ContentPane.GetSelection())
			if tabName.find("*") <> -1:
				self.ContentPane.SetPageText(self.ContentPane.GetSelection(), tabName.replace(" *", ""))
				self.SetTitle(self.ContentPane.GetPageText(self.ContentPane.GetSelection()) + " - mu " + self.version)
		else: 
			self.onFileSaveAs(event)

	# Save As
	def onFileSaveAs(self, event): 
		dlg = wx.FileDialog(self, "Save As", ".", "", "*.*", wx.SAVE)
		if dlg.ShowModal() == wx.ID_OK:
			self.SaveFile(dlg.GetPath(), self.ActiveEditor().GetText())

			# update the list buffer thingey with the correct path 
			# and update the tab so we know the files name.
			self.filesOpen[self.ContentPane.GetSelection()][0] = dlg.GetPath()
			pageName = os.path.split(dlg.GetPath())[1]
			self.ContentPane.SetPageText(self.ContentPane.GetSelection(), pageName) # update tab
			self.SetTitle(self.ContentPane.GetPageText(self.ContentPane.GetSelection()) + " - mu " + self.version) # update window title
			self.Title = pageName + " - mu " + self.version
		dlg.Destroy()

	def onFileSaveAll(self, event):
		logging.info("onFileSaveAll()")
		# This should be the same as Close All, without the closing part.

	def onFileNewProject(self, event):
		dlg = wx.FileDialog(self, "New Project", "Projects", "", "*.muproject", wx.SAVE)
		if dlg.ShowModal() == wx.ID_OK:
			#logging.info("new " + dlg.GetPath())  

			# Create a directory with given project name
			# Create the project file within that directory
			projectName = os.path.splitext(dlg.GetFilename())[0]
			projectPath = os.path.split(dlg.GetPath())[0]

			#print(projectPath) 
			#print(projectName)

			os.mkdir(projectPath + "/" + projectName)

			projectFile = {
				"Project Name" : projectName,
				"Project Description" : "A description of this project goes here.",
			}
			plistlib.writePlist(projectFile, projectPath + "/" + projectName + "/" + projectName + ".muproject")

		dlg.Destroy()
		#self.debug("onFileNewProject()")

	def loadProject(self, projectFilePath):
		#print 'NOW LOADING PROJECT: ' + projectFilePath
		#imglist = wx.ImageList(16, 16, True, 2)
		#imglist.Add(wx.ArtProvider_GetBitmap(wx.ART_FOLDER, wx.ART_OTHER, wx.Size(16,16)))
		#imglist.Add(wx.ArtProvider_GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, wx.Size(16,16)))

		#tree = wx.TreeCtrl(self, -1, wx.Point(0, 0), wx.Size(160, 250), wx.TR_DEFAULT_STYLE | wx.NO_BORDER)
		#tree.AssignImageList(imglist)
		#root = tree.AddRoot("Sample Project",0)
		#items = []

		#items.append(tree.AppendItem(root, "Images", 0))
		#items.append(tree.AppendItem(root, "Docs", 0))
		#for d in xrange(len(items)):
		#	id = items[d]
		#	tree.AppendItem(id, "Subitem 1", 1)
		#tree.Expand(root)
		#tree.Expand(items[1])
		#self.ProjectPane.Add(tree)
		self.showHidePane("ProjectPane")


	def onFileNewProjectFromTemplateEditTemplates(self, event):
		logging.info("onFileNewProjectFromTemplateEditTemplates()")

	def onFileCloseProject(self, event):
		logging.info("onFileCloseProject()")

	def onFilePrint(self, event):
		logging.info("onFilePrint()")

	def onFileExit(self, event):
		logging.info("onFileExit()")
		self.OnMuClose(event)

	# Edit Menu Events...

	def onEditUndo(self, event): self.ActiveEditor().Undo()
	def onEditRedo(self, event): self.ActiveEditor().Redo()
	def onEditCut(self, event): self.ActiveEditor().Cut()
	def onEditCopy(self, event): self.ActiveEditor().Copy()
	def onEditPaste(self, event):
		self.ActiveEditor().Paste()
		# this code is duplicated from onEditorKeyDown().
		tabName = self.ContentPane.GetPageText(self.ContentPane.GetSelection())
		if tabName.find("*") == -1:
			self.ContentPane.SetPageText(self.ContentPane.GetSelection(), tabName + " *")
			self.SetTitle(self.ContentPane.GetPageText(self.ContentPane.GetSelection()) + " - mu " + self.version)

	def onEditSelectAll(self, event): self.ActiveEditor().SelectAll()

	def onEditModeInsert(self, event):
		logging.info("EditToggleOvertype")

	def onEditModeOverwrite(self, event): 
		logging.info("onEditModeOverwrite()")

	def onEditDefine(self, event):
		selectedTerm = self.ActiveEditor().GetSelectedText()
		if selectedTerm.strip() <> "":
			urlenc = urllib.quote(self.ActiveEditor().GetSelectedText())
			self.launchURL("http://dictionary.reference.com/browse/" + selectedTerm)
			#if selectedTerm[:7] == "http://" or selectedTerm[:4] == "www.":
			#	self.launchURL(selectedTerm)
			#else:
			#		self.launchURL("http://www.google.com/search?q=" + urlenc)

	def onEditFind(self, event):
		data = wx.FindReplaceData()
		self.FindDialog = wx.FindReplaceDialog(self, data, "Find")
		self.FindDialog.data = data  # save a reference to it...
		self.FindDialog.data.SetFlags(wx.FR_DOWN)
		self.FindDialog.data.SetFindString(self.ActiveEditor().GetSelectedText())
		self.FindDialog.Show(True)

	# destroy the find / find and replace dialog boxes
	def onEditFindClose(self, event):
		self.FindDialog.Destroy()

	def onEditFindPrevious(self, event):
		self.debug("onEditFindPrevious()")

	def onEditFindReplaceAll(self, event):
		data = wx.FindReplaceData()
		self.FindDialog = wx.FindReplaceDialog(self, data, "Replace All", wx.FR_REPLACEDIALOG)
		self.FindDialog.data = data
		self.FindDialog.Show(True)

	def onEditFindReplaceInSelection(self, event):
		data = wx.FindReplaceData()
		self.FindDialog = wx.FindReplaceDialog(self, data, "Replace In Selection", wx.FR_REPLACEDIALOG)
		self.FindDialog.data = data
		self.FindDialog.Show(True)

	def onEditPreferences(self, event):
		floating_pane = self._mgr.GetPane("settings").Float().Show()
		self._mgr.Update()

	# http://www.wxpython.org/docs/api/wx.aui.AuiPaneInfo-class.html
	def onViewProject(self, event): self.showHidePane("ProjectPane")
	def onViewOutput(self, event): self.showHidePane("OutputPane")

	def showHidePane(self, pane):
		if self._mgr.GetPane(pane).IsShown():
			self._mgr.GetPane(pane).Hide()
		else:
			self._mgr.GetPane(pane).Show()
		self._mgr.Update()

	def onViewFontBigger(self, event):
		self.ActiveEditor().SetZoom(self.ActiveEditor().GetZoom() + 1)

	def onViewFontSmaller(self, event):
		self.ActiveEditor().SetZoom(self.ActiveEditor().GetZoom() - 1)

	def onViewGutterFolding(self, event):
		#self.debug("onViewGutterFolding()")
		#if fold margin width > 0:
		#	unfold all
		pass

	def onViewLineNumbers(self, event):
		if self.ActiveEditor().GetMarginType(0) == 1:
			self.ActiveEditor().SetMarginType(0, 0)
			#self.ActiveEditor().SetMarginWidth(0, 0) # "hide" the margin?
			#self.ActiveEditor().SetMarginVisible(0,0)
		else:
			self.ActiveEditor().SetMarginType(0, 1)
			self.ActiveEditor().SetMarginWidth(0, 32) # "show" the margin?

	def onViewSoftWrap(self, event):
		if self.ActiveEditor().GetWrapMode() == 1:
			self.ActiveEditor().SetWrapMode(0)
		else:
			self.ActiveEditor().SetWrapMode(1)

	def onViewWrapColumnShowIndicator(self, event):
		if self.ActiveEditor().GetEdgeMode() == 1:
			self.ActiveEditor().SetEdgeMode(0)
		else:
			self.ActiveEditor().SetEdgeMode(1)
			self.ActiveEditor().SetEdgeColumn(78)

	def onViewWrapColumnSetColumn40(self, event):
		self.ActiveEditor().SetEdgeMode(1)
		self.ActiveEditor().SetEdgeColumn(40)

	def onViewWrapColumnSetColumn78(self, event):
		self.ActiveEditor().SetEdgeMode(1)
		self.ActiveEditor().SetEdgeColumn(78)

	def onViewWrapColumnSetColumnOther(self, event):
		logger.index("onViewWrapColumnSetColumnOther()")
		column = self.cleanInteger(self.textEntry("At what column do you want to show the wrap column?", "Set Wrap Indicator Column"))
		self.ActiveEditor().SetEdgeColumn(self.cleanInteger(column)) 

	def onViewLineEndings(self, event):
		self.ActiveEditor().SetViewEOL(not self.ActiveEditor().GetViewEOL())

	def onShowInvisibles(self, event):
		self.ActiveEditor().SetViewWhiteSpace(not self.ActiveEditor().GetViewWhiteSpace())

	def onViewFoldAll(self, event):
		self.ActiveEditor().FoldAll()

	def onNavigateNextFileTab(self, event): 
		self.ContentPane.AdvanceSelection(1)

	def onNavigatePreviousFileTab(self, event):
		# Advance Selection doesnt seem to accept negative 
		# numbers for going backwards, so lets get the current
		# tabs index and subtract one from it.
		currentTabIndex = self.ContentPane.GetSelection()
		previousTabIndex = currentTabIndex -1
		self.ContentPane.SetSelection(previousTabIndex)

	def onNavigateGoToLine(self, event):
		try:
			line = self.cleanInteger(self.textEntry("Enter a line number to go to", "Go To"))
			if line > 0: self.ActiveEditor().GotoLine(int(line) - 1) # GoToLine method starts with zero so we end up one line past where we wanted to go.
		except ValueError:
			logging.error(ValueError)

	def onNavigateSelectedFile(self, event):
		self.index("onNavigateSelectedFile()")
	def onHelpMuHelp(self, event):
		self.launchURL("file:///" + self.muroot + "/docs/mu.html")
	def onHelpAbout(self, event):
		message = '''
mu
(c)2008 Eric Chastain (mu@ericchastain.org)
All rights reserved.

Released under the BSD License.

Hopefully you can get something useful out of this. 
If so let me know I would enjoy hearing from you.
'''
		d = wx.MessageDialog(self, message, "About mu", wx.OK | wx.ICON_INFORMATION)
		d.ShowModal()
		d.Destroy()

	#def onToolsGo(self, event):
	#	# based on the given file extension, try to figure out what to do.
	#	# For now im only interested in running python scripts and loading html pages into a browser.
	#	logging.info("onToolsGo()")
	#	filePath = self.filesOpen[self.ContentPane.GetSelection()][0]
	#	tabName = self.ContentPane.GetPageText(self.ContentPane.GetSelection())
	#	fileExtension = os.path.splitext(tabName)[1]
	#	if fileExtension == ".html" or fileExtension == ".htm":
	#		self.launchURL("file:///" + filePath)
	#	if fileExtension == ".py":
	#		logging.info("running " + chr(13) + filePath + chr(13))	
	#	self.LoadFileTemplates()

	# Enable / Disable menus when all files are closed.
	def switchMenus(self):
		# Check the file buffers so we dont have to pass any params in
		if len(self.filesOpen) == 0: s = False
		else: s = True

		# File
		self.File_Close.Enable(s)
		self.File_CloseAllTabs.Enable(s)
		self.File_Save.Enable(s)
		self.File_SaveAs.Enable(s)
		self.File_SaveAll.Enable(s)
		self.File_CloseProject.Enable(s)

		# Edit
		self.Edit_Undo.Enable(s)
		self.Edit_Redo.Enable(s)
		self.Edit_Cut.Enable(s)
		self.Edit_Copy.Enable(s)
		self.Edit_Paste.Enable(s)
		self.Edit_SelectAll.Enable(s)
		self.Edit_FindFind.Enable(s)
		self.Edit_FindNext.Enable(s)
		self.Edit_FindPrevious.Enable(s)
		self.Edit_FindReplaceAll.Enable(s) 
		self.Edit_FindReplaceInSelection.Enable(s) 

		# View
		self.View_FontBigger.Enable(s)
		self.View_FontSmaller.Enable(s)
		self.View_GutterFoldings.Enable(s)
		self.View_GutterLineNumbers.Enable(s)
		self.View_SoftWrap.Enable(s)
		self.View_WrapColumnShowIndicator.Enable(s)
		self.View_WrapColumn40.Enable(s)
		self.View_WrapColumn78.Enable(s)
		self.View_WrapColumnOther.Enable(s)
		self.ViewLineEndings.Enable(s)
		self.ViewInvisibles.Enable(s)
		self.ViewFoldAll.Enable(s)

		# Navigation
		self.Navigation_NextFileTab.Enable(s)
		self.Navigation_PreviousFileTab.Enable(s)
		self.Navigation_GoToLine.Enable(s)
		#self.Navigation_OpenSelectedFile.Enable(s)

	def OnClose(self, event):
		self._mgr.UnInit()
		del self._mgr
		self.Destroy()

	def GetDockArt(self): return self._mgr.GetArtProvider()

	# a stupidly simple feature I have always thought would be nice.
	def onFindOnWeb(self, event):
		selectedTerm = self.ActiveEditor().GetSelectedText()
		if selectedTerm.strip() <> "":
			urlenc = urllib.quote(self.ActiveEditor().GetSelectedText())
			if selectedTerm[:7] == "http://" or selectedTerm[:4] == "www.":
				self.launchURL(selectedTerm)
			else:
					self.launchURL("http://www.google.com/search?q=" + urlenc)


	# ====================================================================
	# Populate Tree Control
	# ====================================================================

	def CreateTreeCtrl(self):
		#imglist = wx.ImageList(16, 16, True, 2)
		#imglist.Add(wx.ArtProvider_GetBitmap(wx.ART_FOLDER, wx.ART_OTHER, wx.Size(16,16)))
		#imglist.Add(wx.ArtProvider_GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, wx.Size(16,16)))

		tree = wx.TreeCtrl(self, -1, wx.Point(0, 0), 
		  wx.Size(160, 250), wx.TR_DEFAULT_STYLE | wx.NO_BORDER)
		#tree.AssignImageList(imglist)
		#root = tree.AddRoot("Sample Project",0)
		#items = []

		#items.append(tree.AppendItem(root, "Images", 0))
		#items.append(tree.AppendItem(root, "Docs", 0))
		#for d in xrange(len(items)):
		#	id = items[d]
		#	tree.AppendItem(id, "Subitem 1", 1)
		#tree.Expand(root)
		#tree.Expand(items[1])
		return tree

	def CreateStyledEditor(self):
		return EditorControl(self, self)

	# http://www.wxpython.org/docs/api/wx.aui.AuiNotebook-class.html
	def InitContentPane(self):
		self.ContentPane = wx.aui.AuiNotebook(self)
		return self.ContentPane

	# Add a blank tab or open filePath into a new tab.
	def AddTab(self, filePath=""):
		if filePath == "": # New File
			fileCount = self.ContentPane.GetSelection() + 2
			if fileCount == 0: fileCount = 1
			editor = EditorControl(self.ContentPane, self)

			self.ContentPane.AddPage(editor, "Untitled " + str(fileCount), -1)
			editor.SetText("")
			self.filesOpen.append(["",0]) # building our file list
		else:

			# If theres only a single, non-dirty tab that is titled 
			# "Untitled 1" lets close it before opening a new one. This should 
			# make the editor behave more like SciTE when you open it theres 
			# a blank page but when you immediately open another its replaced 

			# If only one file open and it isnt dirty
			tabName = self.ContentPane.GetPageText(0)
			if tabName == "Untitled 1" \
			  and len(self.filesOpen) == 1 \
			  and self.filesOpen[0][1] == 0:
				self.filesOpen.pop(0)
				self.ContentPane.DeletePage(0)

			pageName = os.path.split(filePath)[1]
			pageContent = self.OpenFile(filePath)
			editor = EditorControl(self.ContentPane, self)
			self.ContentPane.AddPage(editor, pageName, -1)
			editor.SetText(pageContent)

			self.filesOpen.append([filePath, 0]) # building our file list

			# Add the newly opened item to the recent items list.
			self.AddToRecentItems(filePath)

		# Theme it based on current theme
		# It may pay off to add a GetTheme method to return the currently applied theme.
		# eventually I may want to change this to apply different themes to different
		# tabs by file type.
		themeDirectory = self.configuration["Paths"]["Themes"]
		theme = self.configuration["UI"]["Theme"]
		self.ActiveEditor().SetTheme(themeDirectory + "/" + theme, self.GetCurrentFileExtension())
		self.switchMenus()

		self.ActiveEditor().SetFocus()

	# Save :)
	def SaveFile(self, path, data):
		try:
			self.ActiveEditor().SaveFile(path)
		except:
			print "An error occurred saving the file."

	# Open :) 
	# Should I rewrite this using LoadFile?
	def OpenFile(self, path):
		#self.ActiveEditor().LoadFile(path)
		file = open(path, "r")
		fileData = file.read()
		file.close()
		return fileData

	def launchURL(self, url):
		if url <> "":
			if sys.platform == "win32" or sys.platform == "darwin":
				webbrowser.open(url)
			else:
				print "How do i launch the browser in linux?"

	def textEntry(self, msg, title):
		msgBox=wx.TextEntryDialog(self, msg, title)
		if msgBox.ShowModal() == wx.ID_CANCEL:
			msgBox.Destroy()
			return ""
		res= msgBox.GetValue()
		msgBox.Destroy()
		return res

	def cleanInteger(self, str):
		try:
			return int(str)
		except(ValueError):
			return 0

	# ====================================================================
	# Event Handlers
	# ====================================================================

	def debug(self, s): print(s)

	# Find (and Replace)
	# This method is hooked to the event handlers for the find and find and replace dialogs.
	# All this does really is figure out what to pass on to goFind() to save it some work.
	# Im not a huge fan of this find dialog, I may eventually try to implement some simpler incremental search in place of it.
	# *** Look at the onfind method on this page, may help: http://svn.dabodev.com/dabo/tags/dabo-0.1/ui/uiwx/uiApp.py
	# ** Another update, using the above code and adapting it to the sytled text control I got find working finally.
	def onFind(self, event):
		'''
		eventType = event.GetEventType()
		findText = ""
		replacementText = ""

		# get text to find (and optionally text to replace)
		if eventType == wx.wxEVT_COMMAND_FIND or eventType == wx.wxEVT_COMMAND_FIND_NEXT:
			findText = event.GetFindString()
		elif eventType == wx.wxEVT_COMMAND_FIND_REPLACE or eventType == wx.wxEVT_COMMAND_FIND_REPLACE_ALL:
			findText = event.GetFindString()
			replacementText = "Replace text: %s" % event.GetReplaceString()
		else:
			return "" #wtf?

		# get flags
		flags = event.GetFlags()
		if flags == 0: print "0 look up for " + findText
		elif flags == 1: print "1 look down for " + findText
		elif flags == 2: print "2 look up and match whole word only for " + findText
		elif flags == 3: print "3 look down and match whole word only for " + findText
		elif flags == 4: print "4 look up matching case for " + findText
		elif flags == 5: print "5 look down matching case for " + findText
		elif flags == 6: print "6 look up matching whole word only for and matching case of " + findText
		elif flags == 7: print "7 look down matching whole word only for and matching case of " + findText
		else: print "wtf?"
		'''

		flags = event.GetFlags()
		findString = event.GetFindString()
		downwardSearch = (flags & wx.FR_DOWN) == wx.FR_DOWN
		wholeWord = (flags & wx.FR_WHOLEWORD) == wx.FR_WHOLEWORD
		matchCase = (flags & wx.FR_MATCHCASE) == wx.FR_MATCHCASE

		currentPos = self.ActiveEditor().GetCurrentPos()

		if downwardSearch:
			value = self.ActiveEditor().GetText()[currentPos:]
		else:
			value = self.ActiveEditor().GetText()[0:currentPos]
			value = list(value)
			value.reverse()
			value = "".join(value)
			findString = list(findString)
			findString.reverse()
			findString = "".join(findString)

		if not matchCase:
			value = value.lower()
			findString = findString.lower()

		result = value.find(findString)

		if result >= 0:
			if downwardSearch:
				self.ActiveEditor().SetSelection(currentPos+result, currentPos+result+len(findString))
			else:
				self.ActiveEditor().SetSelection(currentPos-result, currentPos-result-len(findString))
		else:

			# NOTE: This now appears to work pretty well but is fucking nasty. Refactor it and create a nice
			# reusable reverse function that i can call to make these things make more sense.
			# Note: The findString[::-1] below just reverses it, not the code above that turns the stirng into a list then reverses it, thats dumb 

			show_error = 0

			if downwardSearch:
				if self.ActiveEditor().GetText()[:].lower().find(findString.lower()) >= 0:
					self.ActiveEditor().GotoLine(0) # go to first line
				else:
					show_error = 1
			else:
				if self.ActiveEditor().GetText()[:].lower().find(findString[::-1].lower()) >= 1:
					self.ActiveEditor().GotoLine(self.ActiveEditor().GetLineCount() - 1) # go to last line
				else:
					show_error = 1

			if show_error == 0:
				self.onFind(event) # to avoid landing on the first or last line, go ahead and run this again.
			else:
				msg = "The value you specified doesnt appear in this document."
				d = wx.MessageDialog(self, msg, "Not Found", wx.OK | wx.ICON_INFORMATION)
				d.ShowModal()
				d.Destroy()
				self.OnFindClose(event) # In windows, the Find dialog looses focus so you have to use your mouse to use it again, avoid that by just closing it.

			'''

			if self.ActiveEditor().GetText()[:].lower().find(findString.reverse().lower()) >= 1:

				# TODO: This is still a little buggy.
				#If you are searching up it doesnt wrap back down to the bottom if theres only one occurrence of the string?
				#I image this bug lives in the line...
				#if self.ActiveEditor().GetText().find(findString) >= 0:

				# In my opinion the most elegant approach is to just continue finding as long as the user clicks find next.
				# Dont ask if the user wants to start at the beginning or end of the document, they probably do or they
				# wouldnt have clicked find next again.

				if downwardSearch:
					self.ActiveEditor().GotoLine(0) # go to first line
				else:
					self.ActiveEditor().GotoLine(self.ActiveEditor().GetLineCount() - 1) # go to last line

				self.onFind(event) # to avoid landing on the first or last line, go ahead and run this again.

			else:

				msg = "The value you specified doesnt appear in this document."
				d = wx.MessageDialog(self, msg, "Not Found", wx.OK | wx.ICON_INFORMATION)
				d.ShowModal()
				d.Destroy()
				self.OnFindClose(event) # In windows, the Find dialog looses focus so you have to use your mouse to use it again, avoid that by just closing it.
			'''

	def goFind(self, searchText = ""):
		fieldText = self.ActiveEditor().GetText()
		offset = fieldText.find(searchText)
		if offset != -1:
			self.ActiveEditor().SetSelection(offset, offset + len(searchText))

	def OnFindClose(self, event):
		event.GetDialog().Destroy()

class mu(wx.App):
	def OnInit(self):
		frame = MU(None, wx.ID_ANY, "mu", size=(800, 600))
		frame.Show()
		return True  

if __name__ == "__main__":
	mu = mu(0)
	mu.MainLoop()
